Skip to content

fix(sso): clear broker session on customer logout#1329

Merged
superdav42 merged 1 commit into
mainfrom
fix/sso-background-logout
May 31, 2026
Merged

fix(sso): clear broker session on customer logout#1329
superdav42 merged 1 commit into
mainfrom
fix/sso-background-logout

Conversation

@superdav42

@superdav42 superdav42 commented May 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Keep customer-site logout on the customer subsite instead of sending the browser to the main site.
  • Add an SSO logout handoff that calls the main-site /sso-logout endpoint in the background with a short-lived signed token.
  • Set wu_sso_denied for 5 minutes after logout to prevent immediate SSO re-login, and clear it on successful normal or SSO login.
  • Cover the new logout endpoint/token behavior in SSO_Test.

Verification

  • vendor/bin/phpcs inc/sso/class-sso.php tests/WP_Ultimo/SSO/SSO_Test.php — 0 errors; existing warnings only.
  • vendor/bin/phpunit --filter SSO_Test — OK (81 tests, 132 assertions).
  • Pre-commit hook — PHPCS + PHPStan passed for staged PHP files.

Notes

  • Existing unrelated local changes were preserved in stash pre-clean-sso-background-logout-pr; this PR is based on current origin/main and includes only SSO logout changes.

Summary by CodeRabbit

  • New Features

    • SSO now supports background cross-domain logout flow, ensuring users are logged out across all connected sites simultaneously.
    • Enhanced with secure token validation and coordination between main and customer sites.
  • Tests

    • Added comprehensive test coverage for SSO logout behavior and token validation.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

SSO now implements a background cross-domain logout flow. The startup registers logout-related hooks, route handling decorates logout redirects with signaling parameters, main-site and broker endpoints validate tokens and log out users, and existing auth/script flows integrate to enqueue and execute background logout calls.

Changes

SSO Background Logout

Layer / File(s) Summary
Logout hook registration & action routing
inc/sso/class-sso.php
Startup wires logout grant and logout handler hooks plus a logout_redirect filter, and the SSO action regex is updated to recognize -logout suffix; fallback logic also handles $sso_path-logout endpoints.
Logout redirect decoration & endpoint handlers
inc/sso/class-sso.php
handle_logout_redirect() decorates customer logout redirects with wu_sso_logout, loggedout=true, and a short-lived token; handle_server_logout() validates the token, logs out the user, clears transient, sets denial cookie, and returns transport-specific response; handle_broker_logout() clears token and denial cookie; helpers generate/validate HMAC-signed logout tokens (site transient–backed, single-use), detect admin logout redirects, set/clear the wu_sso_denied cookie, and enqueue an inline script for background logout calls.
Logout integration into existing SSO flows
inc/sso/class-sso.php
After successful cookie-less SSO token handling, the wu_sso_denied cookie is cleared; logout query parameters are marked removable in URL cleanup; script enqueue logic detects wu_sso_logout + wu_sso_logout_token and enqueues the background logout script early.
Logout test coverage
tests/WP_Ultimo/SSO/SSO_Test.php
New tests validate logout token generation and validation, logout endpoint URL structure including JSONP parameters, and handler hook registration; JSONP Content-Type regression test is updated to expect an additional path count (4 → 5).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

status:available, origin:worker, review-feedback-scanned

Poem

🐰 Logouts now dance in the background, quiet and lean,
Cross-domain tokens signed with secrets unseen,
A cookie denied, endpoints that call,
Routes recognize logout—yes, we handle it all!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(sso): clear broker session on customer logout' directly addresses the primary objective of the PR—implementing SSO logout functionality to clear the broker session when a customer logs out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sso-background-logout

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/WP_Ultimo/SSO/SSO_Test.php (1)

128-145: ⚡ Quick win

Add a replay assertion for the logout token.

This only checks the happy path. Since the transient is the replay guard, it would be good to mirror the cookie-less token test here and assert that the same logout token is rejected on a second validation.

Suggested test update
-		$result = $validate->invoke($sso, $generate->invoke($sso, $user_id));
+		$token  = $generate->invoke($sso, $user_id);
+		$result = $validate->invoke($sso, $token);
 
 		$this->assertIsArray($result);
 		$this->assertSame($user_id, $result['user_id']);
 		$this->assertNotEmpty($result['jti']);
-
-		delete_site_transient('wu_sso_logout_' . $result['jti']);
+
+		$second_result = $validate->invoke($sso, $token);
+		$this->assertWPError($second_result);
+		$this->assertSame('invalid_token', $second_result->get_error_code());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/WP_Ultimo/SSO/SSO_Test.php` around lines 128 - 145, The test currently
only verifies a successful logout token validation; add a replay assertion by
invoking validate_sso_logout_token a second time with the same token and
asserting it is rejected (e.g., returns false/null or a non-array result) to
ensure the transient-based replay guard works, then keep the existing
delete_site_transient('wu_sso_logout_' . $result['jti']) cleanup; reference the
generate_sso_logout_token and validate_sso_logout_token reflection calls to
locate where to add the second validation and its assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@inc/sso/class-sso.php`:
- Around line 857-868: The logout-token validation currently leaves the
transient intact; after confirming $jti and $user_id and matching
get_site_transient('wu_sso_logout_' . $jti) === $user_id, immediately invalidate
the transient to make the token single-use (e.g. delete_site_transient or
overwrite it) before returning the ['user_id'=>$user_id,'jti'=>$jti]; reference
the $jti/$user_id variables, the get_site_transient('wu_sso_logout_' . $jti)
check in this method, and note that handle_server_logout() must no longer be
relied on to perform the one-time invalidation.

---

Nitpick comments:
In `@tests/WP_Ultimo/SSO/SSO_Test.php`:
- Around line 128-145: The test currently only verifies a successful logout
token validation; add a replay assertion by invoking validate_sso_logout_token a
second time with the same token and asserting it is rejected (e.g., returns
false/null or a non-array result) to ensure the transient-based replay guard
works, then keep the existing delete_site_transient('wu_sso_logout_' .
$result['jti']) cleanup; reference the generate_sso_logout_token and
validate_sso_logout_token reflection calls to locate where to add the second
validation and its assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 94e5e191-8507-4981-a0b6-5b31ef35ec59

📥 Commits

Reviewing files that changed from the base of the PR and between a528aed and 4244e1b.

📒 Files selected for processing (2)
  • inc/sso/class-sso.php
  • tests/WP_Ultimo/SSO/SSO_Test.php

Comment thread inc/sso/class-sso.php
Comment on lines +857 to +868
if (empty($jti) || $user_id <= 0 || (int) get_site_transient('wu_sso_logout_' . $jti) !== $user_id) {
return new \WP_Error('invalid_token', __('SSO logout token has already been used or is invalid.', 'ultimate-multisite'));
}

if ( ! get_user_by('id', $user_id)) {
return new \WP_Error('user_not_found', __('User not found.', 'ultimate-multisite'));
}

return [
'user_id' => $user_id,
'jti' => $jti,
];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the logout token single-use as soon as it validates.

This token is only invalidated later in handle_server_logout(), and only when the current main-site session matches $user_id. If the background request arrives after that session is already gone, the transient survives for the rest of the 5-minute TTL and the same URL can still log the user out later on replay.

Suggested fix
 		if (empty($jti) || $user_id <= 0 || (int) get_site_transient('wu_sso_logout_' . $jti) !== $user_id) {
 			return new \WP_Error('invalid_token', __('SSO logout token has already been used or is invalid.', 'ultimate-multisite'));
 		}
+
+		delete_site_transient('wu_sso_logout_' . $jti);
 
 		if ( ! get_user_by('id', $user_id)) {
 			return new \WP_Error('user_not_found', __('User not found.', 'ultimate-multisite'));
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (empty($jti) || $user_id <= 0 || (int) get_site_transient('wu_sso_logout_' . $jti) !== $user_id) {
return new \WP_Error('invalid_token', __('SSO logout token has already been used or is invalid.', 'ultimate-multisite'));
}
if ( ! get_user_by('id', $user_id)) {
return new \WP_Error('user_not_found', __('User not found.', 'ultimate-multisite'));
}
return [
'user_id' => $user_id,
'jti' => $jti,
];
if (empty($jti) || $user_id <= 0 || (int) get_site_transient('wu_sso_logout_' . $jti) !== $user_id) {
return new \WP_Error('invalid_token', __('SSO logout token has already been used or is invalid.', 'ultimate-multisite'));
}
delete_site_transient('wu_sso_logout_' . $jti);
if ( ! get_user_by('id', $user_id)) {
return new \WP_Error('user_not_found', __('User not found.', 'ultimate-multisite'));
}
return [
'user_id' => $user_id,
'jti' => $jti,
];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@inc/sso/class-sso.php` around lines 857 - 868, The logout-token validation
currently leaves the transient intact; after confirming $jti and $user_id and
matching get_site_transient('wu_sso_logout_' . $jti) === $user_id, immediately
invalidate the transient to make the token single-use (e.g.
delete_site_transient or overwrite it) before returning the
['user_id'=>$user_id,'jti'=>$jti]; reference the $jti/$user_id variables, the
get_site_transient('wu_sso_logout_' . $jti) check in this method, and note that
handle_server_logout() must no longer be relied on to perform the one-time
invalidation.

@superdav42 superdav42 merged commit 1e83ca4 into main May 31, 2026
8 of 11 checks passed
@superdav42

Copy link
Copy Markdown
Collaborator Author

Summary

  • Keep customer-site logout on the customer subsite instead of sending the browser to the main site.
  • Add an SSO logout handoff that calls the main-site /sso-logout endpoint in the background with a short-lived signed token.
  • Set wu_sso_denied for 5 minutes after logout to prevent immediate SSO re-login, and clear it on successful normal or SSO login.
  • Cover the new logout endpoint/token behavior in SSO_Test.

Verification

  • vendor/bin/phpcs inc/sso/class-sso.php tests/WP_Ultimo/SSO/SSO_Test.php — 0 errors; existing warnings only.
  • vendor/bin/phpunit --filter SSO_Test — OK (81 tests, 132 assertions).
  • Pre-commit hook — PHPCS + PHPStan passed for staged PHP files.

Notes

  • Existing unrelated local changes were preserved in stash pre-clean-sso-background-logout-pr; this PR is based on current origin/main and includes only SSO logout changes.


Merged via PR #1329 to main.
Merged by deterministic merge pass (pulse-wrapper.sh).


aidevops.sh v3.20.5 spent 12m on this as a headless bash routine.

@superdav42 superdav42 added the review-feedback-scanned Merged PR already scanned for quality feedback label May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-feedback-scanned Merged PR already scanned for quality feedback

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant